from MySQLdb import DatabaseError
from django.shortcuts import render, HttpResponse
from rest_framework.decorators import action


def home(request):
    return HttpResponse("Hello World")


import uuid
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView, ListCreateAPIView, RetrieveUpdateDestroyAPIView, ListAPIView, \
    RetrieveAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \
    RetrieveModelMixin
from rest_framework.viewsets import ViewSet, GenericViewSet, ModelViewSet
from rest_framework.response import Response
from api import models
from utils.throttle import IpThrottle
from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning
from utils.auth import BlogAuthentication, QueryParamsAuthentication, HeaderAuthentication, NoAuthentication
from rest_framework.throttling import ScopedRateThrottle


class LoginView(APIView):
    # 这里设置不需要token校验
    authentication_classes = []
    permission_classes = []
    # 对登录接口做限流控制,这里的登录业务只需要通过IP来控制
    # 如果要做限流控制，可以使用 drf 的,也可以使用自己写的，比较灵活
    throttle_classes = [IpThrottle, ScopedRateThrottle]
    # 这里可以写多个，例如还有 UserThrottle
    throttle_scope = 'login'  # 自定义作用域名称

    def post(self, request):
        # 1、接收用户传递的 用户名与 密码
        print(request.query_params)
        print(request.data)  # 直接拿到了你传递过来的字节类型数据，并且它这是字典类型数据
        username = request.data.get('username')
        password = request.data.get('password')
        # 2、数据库校验
        user_object = models.SysUser.objects.filter(username=username, password=password).first()
        if not user_object:
            """用户不存在"""
            return Response({"code": 500, "message": "用户名或密码错误"})
        # 3、用户名密码正确
        token = str(uuid.uuid4())
        user_object.token = token
        user_object.save()
        return Response({"code": 200, "message": "登录成功", "data": {"token": token}})


from utils.permission import (MyPermission1, MyPermission2, MyPermission3,
                              UserPermission,
                              ManagerPermission, BossPermission)

from utils.view import ExtendView


class UserView(ExtendView):
    # 只有1 2 3能看
    permission_classes = [UserPermission, ManagerPermission, BossPermission]

    def get(self, request):
        print(request.user)
        print(request.auth)
        return Response({"message": "UserView", "method": "get"})


class OrderView(ExtendView):
    # 只有1 2 能看
    permission_classes = [ManagerPermission, BossPermission]

    def get(self, request):
        # print(request.user)
        # print(request.auth)
        return Response({"message": "OrderView", "method": "get"})


class GetVersionView(APIView):
    """
        基于GET参数
        版本控制 配置文件 VERSION_PARAM
        http://127.0.0.1:8001/getVersion/?version=v1
        1、通过url参数传递

    """
    # 这里设置不需要token校验
    authentication_classes = []
    permission_classes = []
    versioning_class = QueryParameterVersioning

    def get(self, request):
        print(request.version)
        #  GETVERSION 是 urls.py 中定义的name名称
        url = request.versioning_scheme.reverse("GETVERSION", request=request)
        print("反向生成URL")
        print(url)
        return Response({"message": "GetVersionView", "method": "get"})


class ApiGetVersionView(APIView):
    """
        基于URL
        版本控制 配置文件 VERSION_PARAM
       http://127.0.0.1:8001/api/v2/getVersion/
        1、通过url参数传递

    """
    # 这里设置不需要token校验
    authentication_classes = []
    permission_classes = []
    versioning_class = URLPathVersioning

    def get(self, request, *args, **kwargs):
        print(request.version)
        print(request.versioning_scheme)
        # 修改反向解析方式（添加version参数）
        url = request.versioning_scheme.reverse(
            "APIGETVERSION",
            request=request,
        )
        print("反向生成URL")
        print(url)
        return Response({"message": "ApiGetVersionView", "method": "get"})


from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
from rest_framework.negotiation import DefaultContentNegotiation


class ParserView(APIView):
    """
    解析器
    """
    authentication_classes = []
    permission_classes = []
    # 所有的解析器
    parser_classes = [JSONParser, FormParser]
    # 根据请求，匹配对应的解析器
    content_negotiation_class = DefaultContentNegotiation

    def post(self, request, *args, **kwargs):
        print(request.data, type(request.data))
        return Response({"message": "ParserView", "method": "post"})


class ImgView(APIView):
    """
    解析器
    """
    authentication_classes = []
    permission_classes = []
    # 解析器
    parser_classes = [MultiPartParser, ]

    def post(self, request, *args, **kwargs):
        print(request.data, type(request.data))
        return Response({"message": "ImgView", "method": "post"})


from rest_framework import serializers, status


class UserListSerializer(serializers.ModelSerializer):
    """
    序列化Model
    这里用户关联了部门，需要展示部门，同时需要展示性别 男 女 但是库里数据是 1 0
    """
    gender_text = serializers.CharField(source='get_gender_display')
    # 这里为什么可以通过  department.title 的方式来获取到部门名称呢,因为 models.ForeignKey
    # 如果在Department表中还通过ForeignKey关联了其他表的数据同理department.title.xxx.xxx
    department_name = serializers.CharField(source='department.title')
    # 把日期格式化一下
    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)

    # 自定义方法
    xxx = serializers.SerializerMethodField()

    # 获取当前绑定的多个标签
    tags = serializers.SerializerMethodField()

    class Meta:
        model = models.SysUser
        # fields = '__all__' 全量数据展示
        fields = ["id", "username", "gender", "gender_text",
                  "department", "department_name", "create_time", "xxx", "tags"]

    def get_xxx(self, obj):
        # return "123123"
        # return obj.id
        return "{}-{}".format(obj.username, obj.department.title)

    def get_tags(self, obj):
        # 方法1 获取 多对多的 tag标签关系
        queryset = obj.tags.filter(is_active=1)
        return [{"id": tag.id, "caption": tag.caption} for tag in queryset]


class UserListViews(APIView):
    """
    序列化 用户列表
    """
    authentication_classes = []
    permission_classes = []

    def get(self, request, *args, **kwargs):
        # 1、获取数据
        queryset = models.SysUser.objects.all()
        # 2、序列化
        serializer = UserListSerializer(queryset, many=True)
        # 3、返回数据
        context = {"status": True, "data": serializer.data, "code": 200}
        return Response(context, status=200)


class D1(serializers.ModelSerializer):
    """
        部门数据序列化
    """

    class Meta:
        model = models.Department
        fields = ["id", "title"]


class Tag1(serializers.ModelSerializer):
    """
        标签数据序列化
    """

    # 自定义方法
    class Meta:
        model = models.Tag
        fields = ["id", "caption"]


class UserListSerializer2(serializers.ModelSerializer):
    """
    序列化Model
    这里用户关联了部门，需要展示部门，同时需要展示性别 男 女 但是库里数据是 1 0
    """
    gender_text = serializers.CharField(source='get_gender_display')
    # 把日期格式化一下
    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)

    # 开始处理
    department = D1(read_only=True)
    tags = Tag1(many=True, read_only=True)

    class Meta:
        model = models.SysUser
        # fields = '__all__' 全量数据展示
        fields = ["id", "username", "gender", "gender_text", "create_time", "department", "tags"]


class UserList2Views(APIView):
    """
    序列化 用户列表
    """
    authentication_classes = []
    permission_classes = []

    def get(self, request, *args, **kwargs):
        # 1、获取数据
        queryset = models.SysUser.objects.all()
        # 2、序列化
        serializer = UserListSerializer2(queryset, many=True)
        # 3、返回数据
        context = {"status": True, "data": serializer.data, "code": 200}
        return Response(context, status=200)


from django.core.validators import EmailValidator, RegexValidator
from rest_framework import exceptions


class DepartmentSerializer(serializers.Serializer):
    """
    序列化Serializer,需要自己写 表里的字段开始校验
    字段必填 required=True
    """
    title = serializers.CharField(required=True,
                                  max_length=100,
                                  min_length=2,
                                  error_messages={
                                      'required': '部门名称不能为空',
                                      'blank': '部门名称不能为空字符',
                                      'max_length': '部门名称过长（不能超过{max_length}字）',
                                      'min_length': '部门名称太短（不能少于{max_length}字）'
                                  })  # 部门名称
    order = serializers.IntegerField(required=True, max_value=100, min_value=1)

    # level = serializers.ChoiceField(
    #     choices=[(1, "初级"), ("2", "中级"), ("3", "高级"), ("4", "特级")])  # 这里 的 1 2 最终都是变成str来处理

    # 邮箱的校验 我们自己写
    # email = serializers.CharField(required=False, validators=[EmailValidator(message="邮箱格式错误")])
    # 邮箱的校验 自定义正则
    # email = serializers.CharField(required=False, validators=[RegexValidator(r"\d+", message="邮箱格式错误")])

    # 邮箱的校验 等于这个
    # email = serializers.EmailField()

    # 自定义钩子
    def validate_title(self, value):
        # 去除前后空格
        value = value.strip()
        # 新增存在性校验
        if models.Department.objects.filter(title=value).exists():
            raise exceptions.ValidationError("部门名称已存在")

        return value

    def validate(self, attrs):
        """
            全部都验证通过了，就会执行到这里
            这里的使用场景：前端传递file对象给API，API在调用阿里云SDK,这时候，如果前端给我们的数据都是正常
            但是由于我们自己这边的参数有些异常，就会调用SDK失败 例如上传一些危改公共信息资源、SDK签名错误
            这里就可以执行  raise exceptions.ValidationError("全局钩子检验失败")
        """
        print("validate=", attrs)
        # api.settings.NON_FIELD_ERRORS_KEY
        # raise exceptions.ValidationError("全局钩子检验失败")
        return attrs


class DepartmentModelSerializerList(serializers.ModelSerializer):
    """
    序列化ModelSerializer,用这个
    """

    class Meta:
        model = models.Department
        fields = '__all__'


class DepartmentModelSerializer(serializers.ModelSerializer):
    """
    序列化ModelSerializer,用这个 不需要自己写表里的字段 校验
    """

    # confirm_password = serializers.CharField(required=True)  # 确认密码

    class Meta:
        model = models.Department
        # fields = '__all__'
        # fields = ["title", "order","confirm_password"]
        fields = ["title", "order"]
        extra_kwargs = {
            "title": {"required": True, "max_length": 100, "min_length": 2, "error_messages":
                {
                    "required": "部门名称不能为空",
                    "blank": "部门名称不能为空字符",
                    "max_length": "部门名称过长（不能超过{max_length}字）",
                    "min_length": "部门名称太短（不能少于{min_length}字）"
                }
                      },
            "order": {"required": True, "max_value": 9999, "min_value": 0, "error_messages":
                {
                    "required": "部门顺序不能为空",
                    "invalid": "请输入有效数字"  # 新增数字类型错误提示
                }
                      },
            # "count": {"validators": [RegexValidator(r"\d+", message="格式错误")]}
        }

    def validate_title(self, value):
        # 去除前后空格
        value = value.strip()
        # 新增存在性校验
        if models.Department.objects.filter(title=value).exists():
            raise exceptions.ValidationError("部门名称已存在")

        return value


class DepartmentViews(APIView):
    """
    序列化 部门列表
    """
    authentication_classes = []
    permission_classes = []

    def get(self, request, *args, **kwargs):
        # 1、获取数据
        queryset = models.Department.objects.all()
        # 2、序列化
        serializer = DepartmentModelSerializerList(queryset, many=True)
        # 3、返回数据
        context = {"status": True, "data": serializer.data, "code": 200}
        return Response(context, status=200)

    # def post(self, request, *args, **kwargs):
    #     """
    #         使用 serializer 来保存数据
    #     """
    #     # 1、获取数据
    #     print(request.data)
    #     # 2、校验数据
    #     serializer = DepartmentSerializer(data=request.data)
    #     if serializer.is_valid():
    #         # 校验通过
    #         print(serializer.validated_data)
    #         models.Department.objects.create(**serializer.validated_data)
    #         context = {"status": True, "data": serializer.validated_data, "code": 200}
    #         return Response(context, status=200)
    #     else:
    #         # 校验不通过
    #         print(serializer.errors)
    #         context = {"status": False, "data": serializer.errors, "code": 500}
    #         return Response(context, status=200)

    def post(self, request, *args, **kwargs):
        """
            使用 ModelSerializer 来保存数据
        """
        # 1、获取数据
        print(request.data)
        # 2、校验数据
        serializer = DepartmentModelSerializer(data=request.data)
        if serializer.is_valid():
            # 校验通过
            print(serializer.validated_data)
            # confirm_password = serializer.validated_data.pop("confirm_password")  # 这里是利用pop 来干掉 多余的字段
            serializer.save(count=100)
            # 这里为什么写count=100,是因为表里需要3个字段，但是我接口只写了2个，需要设置默认值
            context = {"status": True, "data": serializer.validated_data, "code": 200}
            return Response(context, status=200)
        else:
            # 校验不通过
            print(serializer.errors)
            context = {"status": False, "data": serializer.errors, "code": 500}
            return Response(context, status=200)


class UserList3ModelSerializer(serializers.ModelSerializer):
    # class Meta:
    #     model = models.SysUser
    #     fields = ["username", "department", "gender", "tags"]
    # def validate_department(self, value):
    #     print(value) # 获取到部门对象，可以进行判断之类的
    #     return value
    #
    # def validate_tags(self, value):
    #     print(value) # 获取到对应的tags对象，可以进行判断之类的
    #     return value
    #
    # # FK的id问题 如果一定想传递 department_id 那就自己写
    # department_id = serializers.IntegerField()
    #
    # class Meta:
    #     model = models.SysUser
    #     fields = ["username", "department_id", "gender", "tags"]

    # M2M的自定义问题，如果tags需要个性化传参
    department_id = serializers.IntegerField()
    tags = serializers.ListField()

    class Meta:
        model = models.SysUser
        fields = ["username", "department_id", "gender", "tags"]

    def validate_tags(self, value):
        print(value)
        # 这里手动处理
        queryset = models.Tag.objects.filter(id__in=value)
        return queryset


class UserList3Views(APIView):
    """
    序列化 FK 与 M2M操作
    """
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        """
            使用 ModelSerializer 来保存数据
        """
        # 1、获取数据
        print(request.data)
        # 2、校验数据
        serializer = UserList3ModelSerializer(data=request.data)
        if serializer.is_valid():
            # 校验通过
            print(serializer.validated_data)
            serializer.save()
            context = {"status": True, "message": "保存成功", "code": 200}
            return Response(context, status=200)
        else:
            # 校验不通过
            print(serializer.errors)
            context = {"status": False, "data": serializer.errors, "code": 500}
            return Response(context, status=200)


class DpModelSerializer(serializers.ModelSerializer):
    """
    序列化ModelSerializer,用这个 不需要自己写表里的字段 校验
    """

    class Meta:
        model = models.Department
        fields = ["id", "title", "order", "count"]
        extra_kwargs = {
            "id": {"read_only": True},  # 用户创建时，不需要填写ID
            "count": {"write_only": True, "required": True}  # 序列化返回时，不返回给用户看
        }


class DpModel2Serializer(serializers.ModelSerializer):
    """
    序列化ModelSerializer,用这个 不需要自己写表里的字段 校验
    """

    class Meta:
        model = models.Department
        fields = ["id", "title"]


class DpViews(APIView):
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        """
            使用 ModelSerializer 来保存数据
        """
        # 1、获取数据
        print(request.data)
        # 2、校验数据
        serializer = DpModelSerializer(data=request.data)
        if serializer.is_valid():
            # 校验通过
            instance = serializer.save()
            print("instance -S")
            print(instance)
            print("instance -E")
            # 把创建的数据返回
            context = {"status": True, "message": "保存成功", "code": 200,
                       "data": DpModelSerializer(instance=instance).data}
            return Response(context, status=200)
        else:
            # 校验不通过
            print(serializer.errors)
            context = {"status": False, "data": serializer.errors, "code": 500}
            return Response(context, status=200)


class UUSModelSerializer(serializers.ModelSerializer):
    """
        用户注册，
        会提交 用户名、密码、确认密码、性别之类的
        但是我们返回不会把密码返回去的
        gender 输入的时候时1 ，返回需要显示 男女
        role 也是一样的
    """
    # 针对choices 情况
    gender_label = serializers.CharField(source="get_gender_display", read_only=True)
    role_label = serializers.CharField(source="get_role_display", read_only=True)
    gender_info = serializers.SerializerMethodField()  # 个性化返回

    # 针对 FK 情况，返回关联的部门信息
    # department_info = serializers.CharField(source="department.title", read_only=True) # 方法1 直接返回了部门名称
    department_info = D1(source="department",
                         read_only=True)  # 方法2 我这里之前写好了一个 serializers 部门序列化器 返回 {id:1,title:"部门名称"}

    # 针对M2M情况，返回关联的标签信息
    # 方法1：直接返回标签文字列表（使用已有Tag1序列化器）
    tags_info = Tag1(
        source="tags",
        many=True,
        read_only=True
    )
    # 方法2：自定义字段返回激活状态的标签
    active_tags = serializers.SerializerMethodField()

    class Meta:
        model = models.SysUser
        fields = ["id", "username", "password", "role", "role_label", "department", "department_info", "gender",
                  "gender_label",
                  "gender_info", "tags", "tags_info", "active_tags"]
        extra_kwargs = {
            "id": {"read_only": True},  # 用户创建时，不需要填写ID
            "gender": {"write_only": True},  # 用户创建时，不会返回 gender
            "password": {"write_only": True},  # 序列化返回时，不返回给用户看
            "tags": {
                "write_only": True,  # 隐藏原始tags字段的输出
                "queryset": models.Tag.objects.filter(is_active=1)  # 只允许选择激活标签
            }
        }

    # 个性化返回
    def get_gender_info(self, obj):
        return {"label": obj.get_gender_display(), "value": obj.gender}

    def get_active_tags(self, obj):
        """自定义返回激活状态的标签信息"""
        active_tags = obj.tags.filter(is_active=1)
        return [{"id": tag.id, "name": tag.caption} for tag in active_tags]


class UusViews(APIView):
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        """
            使用 ModelSerializer 来保存数据
        """
        # 1、获取数据
        print(request.data)
        # 2、校验数据
        serializer = UUSModelSerializer(data=request.data)
        if serializer.is_valid():
            # 校验通过
            instance = serializer.save()
            print("instance -S")
            print(instance)
            print("instance -E")
            # 把创建的数据返回
            context = {"status": True, "message": "保存成功", "code": 200,
                       "data": UUSModelSerializer(instance=instance).data}
            # 这里如果大家都是使用同一个序列化器时,可以直接写  serializer.data，
            # 但是如果遇上特别复杂的情况，直接再写一个序列化器更简单写
            # context = {"status": True, "message": "保存成功", "code": 200,
            #            "data": serializer.data}
            return Response(context, status=200)
        else:
            # 校验不通过
            print(serializer.errors)
            context = {"status": False, "data": serializer.errors, "code": 500}
            return Response(context, status=200)


from extend.hooks import HooksSerializer


class NbUserModelSerializer(HooksSerializer, serializers.ModelSerializer):
    class Meta:
        model = models.NbUserInfo
        fields = ["id", "name", "age", "gender"]
        extra_kwargs = {
            "id": {"read_only": True},
        }

    # 这样就可以原字段返回值了
    def get_gender(self, obj):
        return obj.get_gender_display()


class NbUserViews(APIView):
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        """
            使用 ModelSerializer 来保存数据
        """
        # 1、获取数据
        print(request.data)
        # 2、校验数据
        serializer = NbUserModelSerializer(data=request.data)
        if serializer.is_valid():
            # 校验通过
            instance = serializer.save()
            print("instance -S")
            print(instance)
            print("instance -E")
            # 把创建的数据返回
            context = {"status": True, "message": "保存成功", "code": 200,
                       "data": NbUserModelSerializer(instance=instance).data}
            # 这里如果大家都是使用同一个序列化器时,可以直接写  serializer.data，
            # 但是如果遇上特别复杂的情况，直接再写一个序列化器更简单写
            # context = {"status": True, "message": "保存成功", "code": 200,
            #            "data": serializer.data}
            return Response(context, status=200)
        else:
            # 校验不通过
            print(serializer.errors)
            context = {"status": False, "data": serializer.errors, "code": 500}
            return Response(context, status=200)


# 博客系统开始
def createTestData(reqest):
    # 创建用户
    # v1 = models.UserInfo.objects.create(username="路人甲", password="123456a")
    # v2 = models.UserInfo.objects.create(username="龙套乙", password="123456a")
    # 创建博客数据
    # models.Blog.objects.create(title="Python全栈入门到放弃", summary="PythonWeb", text="Python全栈XXXX", category=1,
    #                            creator=v1,
    #                            image
    #                            ="xxx/xxx.png")
    #
    # models.Blog.objects.create(title="VUE入门到放弃", summary="高级前端", text="高级前端CSS,HTML,JS", category=5,
    #                            creator=v2, image
    #                            ="xxx/xxx.png")

    # 创建评论数据
    # models.Comment.objects.create(blog_id=1, content="什么是python", user_id=3)
    # models.Comment.objects.create(blog_id=1, content="python是世界上最好的语言", user_id=4, parent_id=17)
    return HttpResponse("创建数据成功")


class UserInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = ["id", "username", "avatar"]


class BlogSerializer(HooksSerializer, serializers.ModelSerializer):
    ctime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", read_only=True)
    # 替换默认的 creator 字段，使用嵌套的 UserInfoSerializer 返回 username + avatar
    creator = UserInfoSerializer(read_only=True)

    class Meta:
        model = models.Blog
        fields = ["id", "category", "image", "title", "text", "summary", "ctime", "creator", "comment_count",
                  "favor_count"]
        extra_kwargs = {
            "id": {"read_only": True},
            "comment_count": {"read_only": True},
            "favor_count": {"read_only": True},  # 用户提交时不参与
            "text": {"write_only": True},  # 用户提交时需要传递
        }

    def get_category(self, obj):
        return obj.get_category_display()


class BlogDetailsSerializer(serializers.ModelSerializer):
    category = serializers.CharField(source="get_category_display")
    ctime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
    creator = UserInfoSerializer(read_only=True)

    class Meta:
        model = models.Blog
        fields = "__all__"


class BlogCommnetSerializer(HooksSerializer, serializers.ModelSerializer):
    create_datetime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", read_only=True)

    class Meta:
        model = models.Comment
        fields = ["id", "user", "content", "create_datetime", "parent"]
        extra_kwargs = {
            "id": {"read_only": True},  # 用户提交时不参与
            "user": {"read_only": True},  # 用户提交时不参与
            "parent": {"required": False},  # 允许 parent_id 可选 ,默认非必填
        }

    # 这里为什么使用 HooksSerializer ,因为评论的时候，需要回传user=id,但是由于我们这里使用相同的Serializer
    # 所以这里需要重写这个方法,这样就不会影响
    def get_user(self, obj):
        return {"id": obj.user.id, "username": obj.user.username, "avatar": obj.user.avatar}


class BlogViews(APIView):
    authentication_classes = [BlogAuthentication]
    permission_classes = []

    def dispatch(self, request, *args, **kwargs):
        print("哈哈哈")
        # 这里是拦截器，可以做很多事
        # 例如审计系统，记录什么人、什么时间、做了什么事；但是有个成熟的依赖库可以记录更加全面的动作
        # django-audit-log：追踪Django模型变化的核心功能
        return super().dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        """获取博客列表"""
        # 1、获取数据
        queryset = models.Blog.objects.all().order_by("-ctime")
        # 2、序列化
        serializer = BlogSerializer(instance=queryset, many=True)
        # 3、返回数据
        context = {"status": True, "data": serializer.data, "code": 200}
        return Response(context, status=200)

    def post(self, request, *args, **kwargs):
        """创建一篇博客"""
        if not request.user:
            context = {"status": False, "message": "用户未登录", "code": 4001}
            return Response(context, status=200)
        serializer = BlogSerializer(data=request.data)
        if not serializer.is_valid():
            context = {"status": False, "message": "数据校验失败", "code": 500, "data": serializer.errors}
            return Response(context, status=200)
        serializer.save(creator=request.user)
        context = {"status": True, "message": "保存成功", "code": 200}
        return Response(context, status=200)


class BlogDetailsViews(APIView):
    authentication_classes = []
    permission_classes = []

    def get(self, request, *args, **kwargs):
        """获取某篇博客的详细信息"""
        # 1、获取ID
        pk = kwargs.get("pk")
        instance = models.Blog.objects.filter(id=pk).first()
        if not instance:
            context = {"status": False, "message": "数据不存在", "code": 500}
            return Response(context, status=200)
        # 2、序列化
        serializer = BlogDetailsSerializer(instance=instance, many=False)
        # 3、返回数据
        context = {"status": True, "data": serializer.data, "code": 200}
        return Response(context, status=200)


class BlogCommentViews(APIView):
    authentication_classes = [BlogAuthentication]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        """获取指定博客的评论列表数据"""
        # 1、获取数据
        # print(kwargs)
        # blog_id = kwargs.get("blog_id")
        # queryset = models.Comment.objects.filter(blog_id=blog_id)
        # # 2、序列化
        # serializer = BlogCommnetSerializer(instance=queryset, many=True)
        # # 3、返回数据
        # context = {"status": True, "data": serializer.data, "code": 200}
        # return Response(context, status=200)
        """获取指定博客的评论列表数据"""
        print(kwargs)
        blog_id = kwargs.get("blog_id")
        queryset = models.Comment.objects.filter(blog_id=blog_id)

        # 序列化所有评论
        serializer = BlogCommnetSerializer(instance=queryset, many=True)
        comments = serializer.data

        # 构建 id -> comment 映射
        comment_map = {comment['id']: comment for comment in comments}

        # 结果列表只包含顶级评论
        top_comments = []

        # 遍历所有评论
        for comment in comments:
            parent_id = comment.get('parent')
            if parent_id and parent_id in comment_map:
                # 是子评论，添加到父级的 children 中
                parent = comment_map[parent_id]
                if 'children' not in parent:
                    parent['children'] = []
                parent['children'].append(comment)
            elif parent_id is None:
                # 是一级评论
                top_comments.append(comment)

        context = {"status": True, "data": top_comments, "code": 200}
        return Response(context, status=200)

    def post(self, request, *args, **kwargs):
        """添加评论"""
        if not request.user:
            context = {"status": False, "message": "用户未登录", "code": 4004}
            return Response(context, status=200)
        # /api/comment/1/
        # {"content":"评论内容"}
        blog_id = kwargs.get("blog_id")
        print(request)
        print(args)
        print(kwargs)
        if not blog_id:
            context = {"status": False, "message": "博客ID不能为空", "code": 500}
            return Response(context, status=200)
        blog_obj = models.Blog.objects.filter(id=blog_id).first()
        if not blog_obj:
            context = {"status": False, "message": "博客不存在", "code": 500}
            return Response(context, status=200)

        serializer = BlogCommnetSerializer(data=request.data)
        if not serializer.is_valid():
            context = {"status": False, "message": "数据格式有误", "data": serializer.errors, "code": 500}
            return Response(context, status=200)

        # 获取 parent_id（推荐从前端 body 中传递）
        parent_id = request.data.get('parent_id')  # 注意字段名与模型定义一致
        parent = None
        if parent_id:
            try:
                parent = models.Comment.objects.get(id=parent_id, blog=blog_obj)
            except models.Comment.DoesNotExist:
                context = {"status": False, "message": "父评论不存在", "code": 500}
                return Response(context, status=200)

        serializer.save(blog=blog_obj, user=request.user, parent=parent)
        # 更新博客的评论数
        blog_obj.comment_count += 1
        blog_obj.save()
        context = {"status": True, "message": "评论成功", "code": 200}
        # 如果需要序列化返回数据 context = {"status": True, "message": "评论成功","data": serializer.data, "code": 200}
        return Response(context, status=200)

    def delete(self, request, *args, **kwargs):
        """
            删除评论
        """
        pass


class BlogFavorSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Favor
        fields = ["id", "blog"]


class BlogFavorViews(APIView):
    """
        点赞
    """
    authentication_classes = [BlogAuthentication, NoAuthentication]
    permission_classes = []

    def post(self, request, *args, **kwargs):
        # {blog:"id"} + 当前已登录的用户信息
        # 先查询是否已赞过了，如果已赞过了，则取消赞
        serializer = BlogFavorSerializers(data=request.data)
        if not serializer.is_valid():
            context = {"status": False, "message": "数据格式有误", "data": serializer.errors, "code": 500}
            return Response(context, status=200)
        # 是否已赞过了,先查询
        blog = serializer.validated_data["blog"]
        user = request.user
        exists = models.Favor.objects.filter(blog=blog, user=user).exists()
        if exists:
            # 取消赞
            models.Favor.objects.filter(blog=blog, user=user).delete()
            blog.favor_count -= 1
            blog.save()
            message = "取消点赞成功"
        else:
            # 不存在则赞
            serializer.save(user=user)
            blog.favor_count += 1
            blog.save()
            message = "点赞成功"
        context = {"status": True, "message": message, "code": 200}
        return Response(context, status=200)


from utils.encrypt import create_password_hash, verify_password


class UserRegisterSerializer(serializers.ModelSerializer):
    confirm_password = serializers.CharField(
        write_only=True,  # 只用于输入，不参与保存
        required=True,
        label="确认密码",
        error_messages={
            "required": "确认密码不能为空",
            "blank": "确认密码不能为空字符",
        }
    )

    class Meta:
        model = models.UserInfo
        fields = ["username", "password", "confirm_password"]
        extra_kwargs = {
            "username": {"required": True, "max_length": 32, "min_length": 2, "error_messages":
                {
                    "required": "用户名不能为空",
                    "blank": "用户名不能为空字符",
                    "max_length": "用户名称过长（不能超过{max_length}字）",
                    "min_length": "用户名称太短（不能少于{min_length}字）"
                }
                         },
            "password": {"required": True, "max_length": 128, "min_length": 6, "error_messages":
                {
                    "required": "密码不能为空",
                    "blank": "密码不能为空字符",
                    "max_length": "密码过长（不能超过{max_length}字）",
                    "min_length": "密码太短（不能少于{min_length}字）"
                }
                         },
        }

    # 这个未知如果接上了验证码、图形码，就可以在这里判断了
    def validate_username(self, value):
        # print(value)
        # print(self.validated_data) 在这个时间点,validated_data是获取不到的，我们只能使用self.initial_data
        # print(self.initial_data)
        if models.UserInfo.objects.filter(username=value).exists():
            raise serializers.ValidationError("用户名已存在-----")
        return value

    def validate(self, attrs):
        username = attrs.get("username")
        password = attrs.get("password")
        confirm_password = attrs.pop("confirm_password")  # 校验完后移除
        # 校验用户名是否已存在
        if models.UserInfo.objects.filter(username=username).exists():
            raise serializers.ValidationError({"username": "用户名已存在"})
        # 校验两次密码是否一致
        if password.strip() != confirm_password.strip():
            raise serializers.ValidationError({"confirm_password": "两次输入的密码不一致"})

        return attrs

    def create(self, validated_data):
        # 使用 Django 的 make_password 函数加密密码
        # 我这里封装了加密存储函数方法直接导入使用就行
        validated_data['password'] = create_password_hash(validated_data['password'])
        print(validated_data)
        # 创建并返回用户实例
        return models.UserInfo.objects.create(**validated_data)


class UserRegisterView(APIView):
    authentication_classes = []
    permission_classes = []
    """
        用户注册
    """

    def post(self, request):
        # 1、提交数据{"username":"xxx","password":"xxx","confirm_password":"xxx"}
        # 2、校验数据
        serializer = UserRegisterSerializer(data=request.data)
        if serializer.is_valid():
            # 3、保存数据
            serializer.save()
            context = {"status": True, "message": "用户注册成功", "code": 200}
            return Response(context, status=200)
        else:
            context = {"status": False, "message": "用户注册失败", "data": serializer.errors, "code": 500}
            return Response(context, status=200)


class UserLoginSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = ["username", "password"]
        extra_kwargs = {
            "username": {"required": True, "error_messages":
                {
                    "required": "用户名不能为空",
                    "blank": "用户名不能为空字符",

                }
                         },
            "password": {
                "required": True,
                "write_only": True,  # 不返回密码字段
                "error_messages": {"required": "密码不能为空", "blank": "密码不能为空字符", }
            },
        }

    def validate_username(self, value):
        if not value or value.strip() == "":
            raise serializers.ValidationError("用户名不能为空")
        return value.strip()

    def validate_password(self, value):
        if not value or value.strip() == "":
            raise serializers.ValidationError("密码不能为空")
        return value

    def validate(self, attrs):
        """
        全局校验：查询用户 + 密码校验
        """
        username = attrs.get("username")
        password = attrs.get("password")

        # 查询用户
        user_instance = models.UserInfo.objects.filter(username=username).first()
        if not user_instance:
            raise serializers.ValidationError({"password": "用户名不存在"})

        # 校验密码
        if not verify_password(password, user_instance.password):
            raise serializers.ValidationError({"password": "密码错误"})

        # 将数据库对象保存到 instance，供后续使用（可选）
        self.instance = user_instance

        return attrs


import uuid


class UserLoginView(APIView):
    authentication_classes = []
    permission_classes = []
    """
        用户登录
    """

    def post(self, request):
        # request.data { "username":"","password":"" }
        serializer = UserLoginSerializer(data=request.data)
        if not serializer.is_valid():
            return Response({
                "status": False,
                "message": "登录失败",
                "data": serializer.errors,
                "code": 500
            }, status=200)

        # 此时密码已通过校验，可以直接拿到用户对象
        user_instance = serializer.instance

        # 生成 Token-伪造的写法
        token = str(uuid.uuid4())
        user_instance.token = token
        user_instance.save()

        # 返回用户信息
        user_serializer = UserInfoSerializer(instance=user_instance)
        return Response({
            "status": True,
            "message": "登录成功",
            "data": {
                "token": token,
                "user": user_serializer.data
            },
            "code": 200
        }, status=200)


# 针对模型设计序列化器
class BookSerializers(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()  # required=False
    date = serializers.DateField(source="pub_date")

    def create(self, validated_data):
        # 添加数据逻辑
        return models.Book.objects.create(**self.validated_data)

    def update(self, instance, validated_data):
        # 更新逻辑#
        models.Book.objects.filter(pk=instance.pk).update(**validated_data)
        return models.Book.objects.get(pk=instance.pk)


# 针对模型设计序列化器
class BookSerializer(serializers.ModelSerializer):
    # 原本库里的字段是 pub_date , 但是前端需要 显示为  create_time 字段
    create_time = serializers.DateField(
        source="pub_date",
        input_formats=["%Y-%m-%d"],
        format="%Y-%m-%d",
        required=False,
        allow_null=True
    )
    price = serializers.DecimalField(
        max_digits=5, decimal_places=2,
        required=True,
        error_messages={
            "required": "价格不能为空",
            "invalid": "请输入正确的金额格式,例如 99.99",
        }
    )

    class Meta:
        model = models.Book
        fields = ["id", "title", "author", "price", "create_time"]
        # exiclude = ["pub_date"] 当fields 字段很多时,需要单独排除掉其中某个时
        extra_kwargs = {
            "id": {"read_only": True},  # 用户提交时不参与
            "title": {"required": True, "max_length": 32, "min_length": 2, "error_messages":
                {
                    "required": "书名不能为空",
                    "blank": "书名不能为空字符",
                    "max_length": "书名称过长（不能超过{max_length}字）",
                    "min_length": "书名称太短（不能少于{min_length}字）"
                }
                      },
            "author": {"required": True, "max_length": 32, "min_length": 2, "error_messages":
                {
                    "required": "作者名称不能为空",
                    "blank": "作者名称不能为空字符",
                    "max_length": "作者名称过长（不能超过{max_length}字）",
                    "min_length": "作者名称太短（不能少于{min_length}字）"
                }
                       }
        }

    def to_representation(self, instance):
        data = super().to_representation(instance)
        pub_date = instance.pub_date
        data['create_time'] = pub_date.strftime("%Y-%m-%d") if pub_date else "暂无出版日期"
        return data


class BookView(APIView):
    authentication_classes = []
    permission_classes = []

    # 序列化
    def get(self, request, *args, **kwargs):
        # 1、获取数据
        queryset = models.Book.objects.all()
        # 2、序列化
        # 构建序列化器对象   instance  做序列化传参  data  做 反序列化传参   BookSerializer(instance=,data=)
        # 在这里 是做序列化
        serializer = BookSerializer(instance=queryset, many=True)
        # 3、返回数据
        context = {"status": True, "data": serializer.data, "code": 200}
        return Response(context, status=200)

    # 反序列化
    def post(self, request, *args, **kwargs):
        # 1、提交数据{"username":"xxx","password":"xxx","confirm_password":"xxx"}
        # 2、校验数据
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            # 3、保存数据
            serializer.save()
            # models.Book.objects.create(**serializer.validated_data) 本质是它
            context = {"status": True, "message": "添加成功", "code": 200}
            return Response(context, status=200)
        else:
            context = {"status": False, "message": "添加失败", "data": serializer.errors, "code": 500}
            return Response(context, status=200)


class BookDetailView(APIView):
    authentication_classes = []
    permission_classes = []

    def get(self, request, id):
        """
        获取指定id的图书信息
        """
        # 1、获取数据
        try:
            instance = models.Book.objects.get(pk=id)
        except models.Book.DoesNotExist:
            return Response({
                "status": False,
                "message": "书籍不存在",
                "code": 500
            }, status=200)
        # 2、序列化
        serializer = BookSerializer(instance=instance, many=False)
        # 3、返回数据
        context = {"status": True, "data": serializer.data, "code": 200}
        return Response(context, status=200)

    def put(self, request, id):
        """
        更新指定id的数据
        :param request:
        :param id:
        :return:
        """
        try:
            instance = models.Book.objects.get(pk=id)
        except models.Book.DoesNotExist:
            return Response({
                "status": False,
                "message": "书籍不存在",
                "code": 500
            }, status=200)

        serializer = BookSerializer(instance=instance, data=request.data)
        if serializer.is_valid():
            serializer.save()
            # models.Book.objects.filter(pk=id).update(**serializer.validated_data)  # 本质是它
            context = {"status": True, "message": "信息更新成功", "data": serializer.data, "code": 200}
            return Response(context, status=200)
        else:
            context = {"status": False, "message": "信息更新失败", "data": serializer.errors, "code": 500}
            return Response(context, status=200)

    def delete(self, request, id):
        try:
            instance = models.Book.objects.get(pk=id)
            instance.delete()
            return Response({
                "status": True,
                "message": "删除成功",
                "code": 200
            }, status=200)
        except models.Book.DoesNotExist:
            return Response({
                "status": False,
                "message": "书籍不存在",
                "code": 500
            }, status=200)


# 使用 GenericAPIView
class BookV2View(GenericAPIView):
    authentication_classes = []
    permission_classes = []

    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    # 序列化
    def get(self, request, *args, **kwargs):
        serializer = self.get_serializer(instance=self.get_queryset(), many=True)
        context = {"status": True, "data": serializer.data, "code": 200}
        return Response(context, status=200)

    # 反序列化
    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            context = {"status": True, "message": "添加成功", "code": 200}
            return Response(context, status=200)
        else:
            context = {"status": False, "message": "添加失败", "data": serializer.errors, "code": 500}
            return Response(context, status=200)


class BookV2DetailView(GenericAPIView):
    authentication_classes = []
    permission_classes = []

    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    # lookup_field = "id"  # 默认是 pk

    def get(self, request, pk):
        """
        获取指定id的图书信息
        """
        serializer = self.get_serializer(instance=self.get_object(), many=False)
        context = {"status": True, "data": serializer.data, "code": 200}
        return Response(context, status=200)

    def put(self, request, pk):
        """
        更新指定id的数据
        """
        serializer = self.get_serializer(instance=self.get_object(), data=request.data)
        if serializer.is_valid():
            serializer.save()
            context = {"status": True, "message": "信息更新成功", "data": serializer.data, "code": 200}
            return Response(context, status=200)
        else:
            context = {"status": False, "message": "信息更新失败", "data": serializer.errors, "code": 500}
            return Response(context, status=200)

    def delete(self, request, pk):
        self.get_object().delete()
        return Response({
            "status": True,
            "message": "删除成功",
            "code": 200
        }, status=200)


# 使用 mixins 混合类封装
class BookV3View(ListModelMixin, CreateModelMixin, GenericAPIView):
    authentication_classes = []
    permission_classes = []

    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    # 序列化
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    # 反序列化
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookV3DetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
    authentication_classes = []
    permission_classes = []

    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    # lookup_field = "id"  # 默认是 pk

    def get(self, request, pk):
        """
        获取指定id的图书信息
        """
        return self.retrieve(request, pk)

    def put(self, request, pk):
        """
        全量更新，指定ID
        """
        return self.update(request, pk)

    def patch(self, request, pk):
        """
        部分更新数据，指定ID
        """
        return self.partial_update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)


# 使用 mixins 混合类封装  升级版本
class BookV4View(ListCreateAPIView):
    authentication_classes = []
    permission_classes = []

    queryset = models.Book.objects.all()
    serializer_class = BookSerializer


class BookV4DetailView(RetrieveUpdateDestroyAPIView):
    authentication_classes = []
    permission_classes = []
    lookup_field = "id"  # 默认是 pk
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer


# ViewSet 类：重新构建分发机制
"""
    path("api/bookV5/", views.BookV5View.as_view({"get": "get_all", "post": "add_object"})),
    re_path("api/bookV5/(?P<id>\d+)",
            views.BookV5View.as_view({"get": "get_object", "put": "update_object", "delete": "delete_object"})),
    我定义路由视图时,明确了 id 这个字段，所以下面传递的时候也是id
    默认是pk也就是id的意思
"""


class BookV5View(ViewSet):
    authentication_classes = []
    permission_classes = []

    def get_all(self, request):
        return Response("查看所有资源")

    def add_object(self, request):
        return Response("添加资源")

    def get_object(self, request, id):
        return Response("查看单一资源")

    def update_object(self, request, id):
        return Response("更新单一资源")

    def delete_object(self, request, id):
        return Response("删除单一资源")


# ModelViewSet 类
"""
    这里我们继承了  GenericViewSet 这个调用类
    在继承 mixins 的 5个混合类，增、删、改、查、查
    通过 重新构建分发机制 
    
    path("api/bookV6/", views.BookV6View.as_view({"get": "list", "post": "create"})),
    re_path("api/bookV6/(?P<id>\d+)",
            views.BookV6View.as_view(
                {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"})),
                
    一个类，写5个功能接口
"""


class BookV6View(GenericViewSet, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,
                 DestroyModelMixin):
    authentication_classes = []
    permission_classes = []

    lookup_field = "id"  # 默认是 pk
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer


"""
    完美代替 BookV6View 版本实现全部功能
"""

from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.pagination import PageNumberPagination


class MyPagination(PageNumberPagination):
    """
        自己写个分页器,继承分页组件类
    """
    page_size_query_param = "page_size"  # 指定控制每页数量的参数
    max_page_size = 10  # 控制每页最大数量,如果你管我一次要100条,我就最多返回10条


class BookV7View(ModelViewSet):
    authentication_classes = []
    permission_classes = []
    lookup_field = "id"
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    # 过滤-指定过滤字段
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["title", "author"]

    # 排序-指定排序字段
    # filter_backends = [OrderingFilter]
    # ordering_fields = ["id", "title", "author", "price", "pub_date"]

    # 分页-指定分页器
    pagination_class = MyPagination

    @action(methods=["get"], detail=True, url_path='lastobject', url_name='lastobject-detail')
    def detail_object(self, request, id=None):
        try:
            raise DatabaseError
            instance = models.Book.objects.get(id=id)
        except models.Book.DoesNotExist:
            return Response({"detail": "书籍不存在"}, status=status.HTTP_404_NOT_FOUND)
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    @action(methods=["post"], detail=False, url_path='batchLastobjects', url_name='lastobject-list')
    def list_objects(self, request):
        queryset = models.Book.objects.all()
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)


class BookV8View(ModelViewSet):
    authentication_classes = []
    permission_classes = []
    lookup_field = "id"
    queryset = models.Book.objects.all()

    # serializer_class = BookSerializer 如果重写 get_serializer_class,这里就不用写了
    # 可以根据self.action 的值，来判断并返回对应需要使用的serializer
    # 作用 不通的方法可以调用不同的序列化器
    def get_serializer_class(self):
        if self.action == "list_objects":
            return BookSerializer
        elif self.action == "detail_object":
            return BookSerializer
        elif self.action == "create":
            return BookSerializer
        else:
            return BookSerializer

    @action(methods=["get"], detail=True, url_path='lastobject', url_name='lastobject-detail')
    def detail_object(self, request, id=None):
        try:
            instance = models.Book.objects.get(id=id)
        except models.Book.DoesNotExist:
            return Response({"detail": "书籍不存在"}, status=status.HTTP_404_NOT_FOUND)
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    @action(methods=["post"], detail=False, url_path='batchLastobjects', url_name='lastobject-list')
    def list_objects(self, request):
        queryset = models.Book.objects.all()
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
